package com.beta.service.impl;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.baomidou.framework.service.impl.SuperServiceImpl;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.toolkit.CollectionUtil;
import com.beta.dto.user.*;
import com.beta.dto.version.ReceiverUpdateDto;
import com.beta.em.ConfirmCodeType;
import com.beta.em.GenderType;
import com.beta.em.UserStatus;
import com.beta.em.UserType;
import com.beta.em.admins.LedgerStatus;
import com.beta.entity.*;
import com.beta.mapper.UsersMapper;
import com.beta.service.*;
import org.apache.commons.lang3.StringUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Users 表数据服务层接口实现类
 */
@Service
public class UsersService extends SuperServiceImpl<UsersMapper, Users> implements IUsersService {

    @Autowired
    private IPushToAppService pushService;

    @Autowired
    private IReceiversService receiversService;

    @Autowired
    private IClientsService clientsService;

    @Autowired
    private ICitiesService citiesService;

    @Autowired
    private IDateTimeService dbService;

    @Autowired
    private IConfirmationCodesService confirmationCodesService;

    @Autowired
    private ICouriersService couriersService;

    @Autowired
    private ICustomerManagersService customerManagersService;

    @Value("${platform.pay.fee}")
    private BigDecimal defaultPlatformPayFeeRatio = new BigDecimal("0.015");

    @Override
    public Users executeCreateUser(String confirmationCode, UserRegisterDto registerDto) {

        verifyConfirmationCode(registerDto.getPhone(), ConfirmCodeType.REGIST, confirmationCode);

        verifyCity(registerDto.getCityCode(), registerDto.getCityName());

        if (existUser(registerDto.getPhone())) {
            throw new ServiceException("E10004");
        }

        Date dbDate = dbService.toDbDate();
        Users userForInsert = new Users()
                .setPhone(registerDto.getPhone())
                .setPhoneNormalized("+86" + registerDto.getPhone())
                .setUserType(registerDto.getUserType())
                .setEncryptedPassword(BCrypt.hashpw(registerDto.getPassword(), BCrypt.gensalt()))
                .setCityCode(registerDto.getCityCode())
                .setCityName(registerDto.getCityName())
                .setStatus(UserStatus.REGISTED.getValue())
                .setCreatedAt(dbDate)
                .setUpdatedAt(dbDate);

        setCustomerManagerInfo(userForInsert, registerDto.getCustomerManagerCode());
        insertSelective(userForInsert);
        return (selectOne(new Users().setPhone(registerDto.getPhone()).setUserType(registerDto.getUserType())));
    }

    private void setCustomerManagerInfo(Users users, String customerManagerCode) {
        if (StringUtils.isBlank(customerManagerCode)) {
            return ;
        }

        CustomerManagers customerManager = customerManagersService.getCustomerManagerByCode(customerManagerCode);
        if (Objects.isNull(customerManager)) {
            throw new ServiceException("E00109");
        }
        users.setCustomerManagerCode(customerManagerCode).setCustomerManagerName(customerManager.getName());
    }

    private boolean existUser(String phone) {
        return selectCount(new Users().setPhone(phone)) > 0;
    }

    private void verifyCity(String cityCode, String cityName) {
        if (citiesService.selectCount(new Cities().setCode(cityCode).setName(cityName)) < 1) {
            throw new ServiceException("E10003");
        }
    }

    private void verifyPasswordConfirmation(String password, String passwordConfirmation) {
        if (StringUtils.isAnyBlank(password, passwordConfirmation) || !StringUtils.equals(password, passwordConfirmation)) {
            throw new ServiceException("user.password.noEquals");
        }
    }

    private void verifyConfirmationCode(String phone, ConfirmCodeType confirmCodeType, String confirmationCode) {
        if (StringUtils.isBlank(confirmationCode)) {
            throw new ServiceException("confirmationCode.blank");
        }
        confirmationCodesService.validateCode(phone, confirmCodeType, confirmationCode);
    }

    @Override
    public void executeResetPassword(String confirmationCode, UserResetPasswordDto resetPasswordDto) {

        ensurePhoneNoExists(resetPasswordDto.getPhone());

        verifyPasswordConfirmation(resetPasswordDto.getPassword(), resetPasswordDto.getPasswordConfirmation());

        verifyConfirmationCode(resetPasswordDto.getPhone(), ConfirmCodeType.RESET, confirmationCode);

        Users dbUser = selectOne(new Users().setPhone(resetPasswordDto.getPhone()));
        if (Objects.isNull(dbUser)) {
            throw new ServiceException("E00113");
        }
        Users users = new Users().setEncryptedPassword(BCrypt.hashpw(resetPasswordDto.getPassword(), BCrypt.gensalt()));
        updateSelective(users, new Users().setPhone(resetPasswordDto.getPhone()));
    }

    private void ensurePhoneNoExists(String phone) {
        // must exists in system
        if (StringUtils.isBlank(phone) || CollectionUtils.isEmpty(selectList(new EntityWrapper<>(new Users().setPhone(phone))))) {
            throw new ServiceException("user.phone.noExists");
        }
    }

    @Override
    public Users executeApplyUser(UserApplyDto applyDTO) {
        String userName = applyDTO.getUsername();
        Integer userId = applyDTO.getId();
        Date toDbDate = dbService.toDbDate();
        Users user = new Users();
        user.setName(applyDTO.getName());
        user.setUsername(userName);
        user.setGender(applyDTO.getGender());
        user.setIdCardNumber(applyDTO.getIdCardNumber());
        user.setBusinessLicenceNumber(applyDTO.getBusinessLicenceNumber());
        user.setUpdatedAt(toDbDate);
        updateSelective(user, new Users().setId(userId));
        Receivers receiver = new Receivers();
        receiver.setAddress(applyDTO.getAddress());
        receiver.setUserName(userName);
        receiver.setUserPhone(applyDTO.getUserPhone());
        receiver.setUserId(userId);
        receiver.setCreatedAt(toDbDate);
        receiver.setUpdatedAt(toDbDate);
        if (receiversService.selectCount(receiver) < 1) {
            receiversService.insert(receiver);
        }
        Users users = selectById(userId);
        if (Objects.isNull(users)) {
            throw new ServiceException("E00007");
        }
        return users;
    }

    @Override
    public void executeBungingUserCid(Integer userId, UserType userType, String cid, String deviceType) {
        validateUserType(userType);
        Clients client = new Clients();
        client.setUserId(userId);
        client.setCid(cid);
        if (UserType.COURIER.equals(userType)) {
            Couriers couriers = getCourierAndCheckNonNull(userId);
            client.setUserName(couriers.getName());
            client.setUserPhone(couriers.getPhone());
        } else {
            Users users = getUserAndCheckNonNull(userId);
            client.setUserName(users.getName());
            client.setUserPhone(users.getPhone());
        }
        Date dbDate = dbService.toDbDate();
        client.setDevice(deviceType);
        client.setCreatedAt(dbDate);
        client.setUpdatedAt(dbDate);
        clientsService.deleteSelective(new Clients().setCid(cid));
        clientsService.insertSelective(client);
    }

    @Override
    public void executeUnBundingCid(String cid) {
        clientsService.deleteSelective(new Clients().setCid(cid));
    }

    @Override
    public Users getAndValidateUser(Integer userId) {
        if (Objects.isNull(userId)) {
            throw new ServiceException("E00106");
        }

        Users users = baseMapper.selectById(userId);
        if (Objects.isNull(users)) {
            throw new ServiceException("E00106");
        }
        return users;
    }

    @Override
    public BigDecimal getDealerPlatformFeeRatio(Users dealerUser) {
        if (Objects.isNull(dealerUser)) {
            throw new ServiceException("E00106");
        }

        if (!UserType.DEALER.getValue().equals(dealerUser.getUserType())) {
            throw new ServiceException("E00108");
        }

        BigDecimal feeRate = dealerUser.getFeeRate();
        if (Objects.isNull(feeRate)) {
            feeRate = defaultPlatformPayFeeRatio;
        }
        return feeRate;
    }

    private void validateUserType(UserType userType) {
        if (Objects.isNull(userType)) {
            throw new ServiceException("E10005");
        }
    }

    private Users getUserAndCheckNonNull(Integer userId) {
        if (Objects.nonNull(userId)) {
            Users users = selectById(userId);
            if (Objects.nonNull(users)) {
                return users;
            }
        }
        throw new ServiceException("E00106");
    }

    private Couriers getCourierAndCheckNonNull(Integer courierId) {
        if (Objects.nonNull(courierId)) {
            Couriers couriers = couriersService.selectById(courierId);
            if (Objects.nonNull(couriers)) {
                return couriers;
            }
        }
        throw new ServiceException("E00106");
    }

    @Override
    public void updateIdCardPicture(Integer userId, String idCardPicture) {
        if (Objects.isNull(userId)) {
            return;
        }

        Users updateEntity = new Users()
                .setId(userId)
                .setIdCardPicture(idCardPicture)
                .setUpdatedAt(dbService.toDbDate());
        baseMapper.updateSelectiveById(updateEntity);
    }

    @Override
    public void deleteIdCardPicture(Integer userId) {
        updateIdCardPicture(userId, "");
    }

    @Override
    public Page<UsersDto> getUsers(Page<UsersDto> page, UserSearchDto userSearchDto) {
        page.setRecords(baseMapper.getUsers(page, userSearchDto.getName(), userSearchDto.getPhone(), userSearchDto.getCityName(), userSearchDto.getStatus(), userSearchDto.getType(), userSearchDto.getCityCode()));
        return page;
    }

    @Override
    public UserDetailDto getUserDetailById(Integer userId) {
        Users users = selectOne(new Users().setId(userId));
        UserDetailDto userDto = new UserDetailDto();
        userDto.setCode(users.getCode());
        userDto.setName(users.getName());
        userDto.setCityName(users.getCityName());
        userDto.setCityCode(users.getCityCode());
        userDto.setLedgerStatus(users.getLedgerStatus());
        userDto.setLedgerStatusText(LedgerStatus.getCnValue(users.getLedgerStatus()));
        userDto.setStatus(UserStatus.getCnValue(users.getStatus()));
        userDto.setGender(GenderType.getCnValue(users.getGender()));
        userDto.setPhoneNormalized(users.getPhoneNormalized());
        userDto.setUsername(users.getUsername());
        userDto.setUserType(users.getUserType());
        userDto.setUserTypeName(UserType.getCnValue(users.getUserType()));
        userDto.setBankCardPicture(users.getBankCardPicture());
        userDto.setBankCardBackPicture(users.getBankCardBackPicture());
        userDto.setIdCardNumber(users.getIdCardNumber());
        userDto.setIdCardPicture(users.getIdCardPicture());
        userDto.setIdCardBackPicture(users.getIdCardBackPicture());
        userDto.setPersonPhoto(users.getPersonPhoto());
        userDto.setBusinessLicenceNumber(users.getBusinessLicenceNumber());
        userDto.setBusinessLicencePicture(users.getBusinessLicencePicture());
        userDto.setBankLisencePicture(users.getBankLisencePicture());
        userDto.setBusinessCertPicture(users.getBusinessCertPicture());
        userDto.setLegalPerson(users.getLegalPerson());
        userDto.setTaxRegPicture(users.getTaxRegPicture());
        userDto.setOrgCodePicture(users.getOrgCodePicture());
        userDto.setCustomerType(users.getCustomerType());
        userDto.setLedgerCode(users.getLedgerCode());
        userDto.setSignedName(users.getSignedName());
        userDto.setFeeRate(users.getFeeRate());
        return userDto;
    }

    @Override
    public Receivers getReceiverById(Integer receiverId) {
        return receiversService.selectById(receiverId);
    }

    @Override
    public Page<Receivers> getReceiversById(Page<Receivers> page, Integer userId) {
        return receiversService.selectPage(page, new EntityWrapper<>(new Receivers().setUserId(userId)));
    }

    @Override
    public void updateReceiver(ReceiverUpdateDto receiverUpdateDto) {
        Receivers receivers = new Receivers().setUserName(receiverUpdateDto.getUserName())
                .setId(receiverUpdateDto.getId())
                .setAddress(receiverUpdateDto.getAddress())
                .setUserPhone(receiverUpdateDto.getUserPhone())
                .setDescription(receiverUpdateDto.getDescription())
                .setUpdatedAt(dbService.toDbDate());
        receiversService.updateSelective(receivers, new Receivers().setId(receiverUpdateDto.getId()));
    }

    @Override
    public void deleteReceiver(Integer receiverId) {
        receiversService.deleteById(receiverId);
    }

    @Override
    public void createReceiver(ReceiverUpdateDto receiverUpdateDto, Integer userId) {
        Receivers receivers = new Receivers().setUserName(receiverUpdateDto.getUserName())
                .setId(receiverUpdateDto.getId())
                .setAddress(receiverUpdateDto.getAddress())
                .setUserPhone(receiverUpdateDto.getUserPhone())
                .setDescription(receiverUpdateDto.getDescription())
                .setUpdatedAt(dbService.toDbDate())
                .setCreatedAt(dbService.toDbDate())
                .setUserId(userId);
        receiversService.insertSelective(receivers);
    }

    @Override
    public void executeActiveUser(Integer userId) {
        updateSelectiveById(new Users()
                .setId(userId)
                .setStatus(UserStatus.ACTIVE.getValue())
                .setActiveAt(dbService.toDbDate())
                .setUpdatedAt(dbService.toDbDate()));
        pushService.pushToSingle(userId, "您的账户信息已经审核通过", UserStatus.ACTIVE.getValue());
    }

    @Override
    public void executeDenyUser(Integer userId) {
        updateSelectiveById(new Users()
                .setId(userId)
                .setStatus(UserStatus.DENIED.getValue())
                .setUpdatedAt(dbService.toDbDate()));
    }

    @Override
    public Map<Integer, Users> getUsersBatch(List<Integer> userIds) {
        if (CollectionUtil.isEmpty(userIds)) {
            return Collections.emptyMap();
        }

        List<Users> users = baseMapper.selectBatchIds(userIds);
        if (CollectionUtils.isEmpty(users)) {
            return Collections.emptyMap();
        }

        return users.stream().collect(Collectors.toMap(Users::getId, Function.identity()));
    }

}